package cn.lg.soar.system.auth.service.impl;

import cn.lg.soar.common.data.dict.DictItem;
import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.system.auth.entity.AccountBind;
import cn.lg.soar.system.auth.entity.User;
import cn.lg.soar.system.auth.mapper.AccountBindMapper;
import cn.lg.soar.system.auth.service.IAccountBindService;
import cn.lg.soar.system.auth.service.IUserService;
import cn.lg.soar.system.config.AccountBindProps;
import cn.lg.soar.system.config.AuthCacheKey;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * AccountBind服务层实现
 * 租户
 * @author luguoxiang
 * @since 2022-04-30
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class AccountBindServiceImpl extends ServiceImpl<AccountBindMapper, AccountBind> implements IAccountBindService {

    @Autowired
    private IUserService userService;

    private final Map<Integer, String> typeMap;

    public AccountBindServiceImpl(AccountBindProps props) {
        Map<Integer, String> typeMap = props.getTypeMap();
        if (typeMap == null) {
            typeMap = new HashMap<>();
        }
        typeMap.put(0, "账号");
        this.typeMap = typeMap;
    }

    @Override
    public Long getUserId(int accountType, String account) {
        if (accountType == 0) {
            // 根据用户名查询
            return userService.getObj(
                    Wrappers.<User>lambdaQuery()
                            .select(User::getId)
                            .eq(User::getUsername, account),
                    x -> Long.parseLong(x.toString())
            );
        }
        AssertUtil.isTrue(typeMap.containsKey(accountType), "不支持的账号类型，请先通过配置文件定义");
        // 根据绑定账号查询用户id
        AccountBind one = getOne(
                Wrappers.<AccountBind>lambdaQuery()
                        .select(AccountBind::getUserId)
                        .eq(AccountBind::getAccountType, accountType)
                        .eq(AccountBind::getAccount, account)
        );
        if (one == null) {
            return null;
        }
        return one.getUserId();
    }

    @Override
    public User getUser(int accountType, String account) {
        if (accountType == 0) {
            return userService.getByUsername(account);
        }
        AssertUtil.isTrue(typeMap.containsKey(accountType), "不支持的账号类型，请先通过配置文件定义");
        // 根据绑定账号查询用户id
        AccountBind one = getOne(
                Wrappers.<AccountBind>lambdaQuery()
                        .select(AccountBind::getUserId)
                        .eq(AccountBind::getAccountType, accountType)
                        .eq(AccountBind::getAccount, account)
        );
        if (one == null) {
            return null;
        }
        // 查询用户
        return userService.getById(one.getUserId());
    }

    @Override
    public void binding(int accountType, String account, long userId) {
        AssertUtil.isFalse(accountType == 0, "不允许绑定类型 0");
        AssertUtil.isTrue(typeMap.containsKey(accountType), "不支持的账号类型，请先通过配置文件定义");
        AccountBind accountBind = new AccountBind();
        accountBind.setAccountType(accountType);
        accountBind.setAccount(account);
        accountBind.setUserId(userId);
        AssertUtil.isTrue(save(accountBind), "绑定失败");
    }

    @Override
    public void unbind(int accountType, String account, long userId) {
        AssertUtil.isFalse(accountType == 0, "不允许解绑类型 0");
        AssertUtil.isTrue(typeMap.containsKey(accountType), "不支持的账号类型，请先通过配置文件定义");
        LambdaQueryWrapper<AccountBind> queryWrapper = Wrappers.<AccountBind>lambdaQuery()
                .eq(AccountBind::getAccountType, accountType)
                .eq(AccountBind::getAccount, account)
                .eq(AccountBind::getUserId, userId);
        AssertUtil.isTrue(remove(queryWrapper), "操作失败");
    }

    @CacheEvict(value = AuthCacheKey.USER_BY_ID, key = "#userId")
    @Override
    public boolean updateLoginInfo(long userId, int accountType, String account) {
        LocalDateTime now = LocalDateTime.now();
        if (accountType != 0) {
            update(
                    Wrappers.<AccountBind>lambdaUpdate()
                            .set(AccountBind::getUpdateTime, now)
                            .set(AccountBind::getLastLoginDate, now)
                            .setSql("login_count = login_count + 1")
                            .eq(AccountBind::getAccountType, accountType)
                            .eq(AccountBind::getAccount, account)
            );
        }
        return userService.update(
                Wrappers.<User>lambdaUpdate()
                        .set(User::getUpdateTime, now)
                        .set(User::getLastLoginDate, now)
                        .set(User::getPwdErrorCount, 0)
                        .setSql("login_count = login_count + 1")
                        .eq(User::getId, userId)
        );
    }

    @Override
    public List<DictItem<Integer>> types() {
        return typeMap.entrySet()
                .stream()
                .map(x -> new DictItem<>(x.getKey(), x.getValue()))
                .sorted(Comparator.comparing(DictItem::getValue))
                .collect(Collectors.toList());
    }

    @Override
    public String getTypeLabel(int accountType) {
        return typeMap.get(accountType);
    }

    @Override
    public List<AccountBind> listByUserId(Long userId) {
        return list(
                Wrappers.<AccountBind>lambdaQuery()
                        .eq(AccountBind::getUserId, userId)
        );
    }
}
